package com.springboot.util;

import static org.apache.commons.lang3.ArrayUtils.isNotEmpty;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;

/**
 * 切面日志
 * 
 * @author fuzy
 *
 */
public final class AopLogger
{

    /** 入口日志 */
    private static final String ENTER_LOG_TEMP = "Enter {}(), args::arg";

    /** 出口日志 */
    private static final String EXIT_LOG = "Exit {}(), return:{}, cost:{}ms";

    private AopLogger()
    {

    }

    /**
     * 切面打印日志，环绕
     * 
     * @param pjp 切入点
     * @return 返回对象
     * @throws Throwable Throwable
     */
    public static Object around(ProceedingJoinPoint pjp) throws Throwable
    {
        boolean doLog = false;
        // 当前业务对象
        Object obj = pjp.getTarget();
        Class<?> clazz = obj.getClass();
        // 获取方法名
        Signature signature = pjp.getSignature();
        String methodName = signature.getName();
        // 进出口日志的参数列表
        List<Object> argList = new ArrayList<>();
        List<Object> returnList = new ArrayList<>();
        // 获取成员日志对象
        StringBuilder s = new StringBuilder();
        String entryLog = null;
        Logger logger = getLogger(clazz, obj);
        if (logger != null)
        {
            // 方法名
            argList.add(methodName);
            //returnList.add(methodName);
            // 当前业务方法的参数列表
            Object[] arguments = pjp.getArgs();
            // 根据参数列表获取当前业务方法
            if (isNotEmpty(arguments))
            {
                StringBuilder sb = new StringBuilder();
                for (Object arg : arguments)
                {
                    EntityUtils.toString(s, arg);
                    argList.add(s.toString());
                    s.setLength(0);
                    sb.append("{}, ");
                }
                entryLog = ENTER_LOG_TEMP.replace(":arg", sb.substring(0, sb.length() - 2));
            }
            else
            {
                entryLog = ENTER_LOG_TEMP.replace(", args::arg", "");
            }
            Method method = ((MethodSignature) signature).getMethod();
            if (method != null)
            {
                // 业务方法是否加入NoLog注释，有则不打日志
                // doLog = method.getAnnotation(NoLog.class) == null;
                doLog = true;
            }
        }
        if (doLog)
        {
            // 打印入口日志
            logger.debug(entryLog, argList.toArray());
        }
        // 执行业务方法
        long start = System.currentTimeMillis();
        Object returnObject = pjp.proceed();
        long end = System.currentTimeMillis();
        if (doLog)
        {
            // 打印出口日志
            EntityUtils.toString(s, returnObject);
            returnList.add(s.toString());
            returnList.add(end - start);
            logger.debug(EXIT_LOG, returnList.toArray());
        }
        return null;
    }

    /**
     * 获取成员日志对象
     * 
     * @param clazz 参数类
     * @param obj 参数对象
     * @return 日志
     */
    private static Logger getLogger(Class<?> clazz, Object obj)
    {
        Logger logger;
        if (clazz == Object.class)
        {
            logger = null;
        }
        else
        {
            try
            {
                Field field = clazz.getDeclaredField("logger");
                field.setAccessible(true);
                logger = (Logger) field.get(obj);
            }
            catch (Exception e)
            {
                logger = null;
            }
            if (logger == null)
            {
                logger = getLogger(clazz.getSuperclass(), obj);
            }
        }
        return logger;
    }

}
